// https://learn.microsoft.com/en-us/typography/opentype/spec/post

use super::*;

#[derive(Clone)]
pub struct Table<'a> {
	pub version: Version16Dot16, // 0x00010000 for version 1.0, 0x00020000 for version 2.0, 0x00025000 for version 2.5 (deprecated), 0x00030000 for version 3.0
	pub italic_angle: Fixed, // Italic angle in counter-clockwise degrees from the vertical. Zero for upright text, negative for text that leans to the right (forward).
	pub underline_position: FWORD, // Suggested y-coordinate of the top of the underline.
	pub underline_thickness: FWORD, // Suggested values for the underline thickness. In general, the underline thickness should match the thickness of the underscore character (U+005F LOW LINE), and should also match the strikeout thickness, which is specified in the OS/2 table.
	pub is_fixed_pitch: u32, // Set to 0 if the font is proportionally spaced, non-zero if the font is not proportionally spaced (i.e. monospaced).
	pub min_mem_type42: u32, // Minimum memory usage when an OpenType font is downloaded.
	pub max_mem_type42: u32, // Maximum memory usage when an OpenType font is downloaded.
	pub min_mem_type1: u32,  // Minimum memory usage when an OpenType font is downloaded as a Type 1 font.
	pub max_mem_type1: u32,  // Maximum memory usage when an OpenType font is downloaded as a Type 1 font.
	// version 0x00020000
	// pub num_glyphs: u16, // Number of glyphs (this should be the same as numGlyphs in 'maxp' table).
	pub glyph_name_index: LazyArray16<'a, u16>, // Array of indices into the string data. See below for details.
	pub string_data: &'a [u8],                  // Storage for the string data.
	// version 0x00025000
	// pub num_glyphs: u16, // Number of glyphs.
	pub offset: LazyArray16<'a, i8>, // Difference between the glyph index and the standard order of the glyph.
}
impl<'a> Table<'a> {
	pub fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		let version = s.read::<Version16Dot16>()?;
		let this = Table {
			version,
			italic_angle: s.read()?,
			underline_position: s.read()?,
			underline_thickness: s.read()?,
			is_fixed_pitch: s.read()?,
			min_mem_type42: s.read()?,
			max_mem_type42: s.read()?,
			min_mem_type1: s.read()?,
			max_mem_type1: s.read()?,
			glyph_name_index: if version.0 == 0x00020000 {
				s.parse_array16()?
			} else {
				s.read_array16(0)?
			},
			string_data: if version.0 == 0x00020000 {
				s.tail()? //
			} else {
				s.read_bytes(0)?
			},
			offset: if version.0 == 0x00025000 {
				s.parse_array16()?
			} else {
				s.read_array16(0)?
			},
		};
		Some(this)
	}
	/// Returns a glyph name by ID.
	pub fn glyph_name(&self, glyph_id: u16) -> Option<&'a str> {
		let mut index = self.glyph_name_index.get(glyph_id)?;
		// 'If the name index is between 0 and 257, treat the name index
		// as a glyph index in the Macintosh standard order.'
		if usize::from(index) < MACINTOSH_NAMES.len() {
			Some(MACINTOSH_NAMES[usize::from(index)])
		} else {
			// 'If the name index is between 258 and 65535, then subtract 258 and use that
			// to index into the list of Pascal strings at the end of the table.'
			index -= MACINTOSH_NAMES.len() as u16;
			self.names().nth(usize::from(index))
		}
	}
	/// Returns a glyph ID by a name.
	pub fn glyph_index_by_name(&self, name: &str) -> Option<u16> {
		let id = if let Some(index) = MACINTOSH_NAMES.iter().position(|n| *n == name) {
			self.glyph_name_index.into_iter().position(|i| usize::from(i) == index)?
		} else {
			let mut index = self.names().position(|n| n == name)?;
			index += MACINTOSH_NAMES.len();
			self.glyph_name_index.into_iter().position(|i| usize::from(i) == index)?
		};
		Some(id as u16)
	}
	pub fn names(&self) -> Names<'a> {
		Names {
			data: self.string_data,
			offset: 0,
		}
	}
}
impl<'a> core::fmt::Debug for Table<'a> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.debug_struct("Table")
			.field("version", &self.version)
			.field("italic_angle", &self.italic_angle)
			.field("underline_position", &self.underline_position)
			.field("underline_thickness", &self.underline_thickness)
			.field("is_fixed_pitch", &self.is_fixed_pitch)
			.field("min_mem_type42", &self.min_mem_type42)
			.field("max_mem_type42", &self.max_mem_type42)
			.field("min_mem_type1", &self.min_mem_type1)
			.field("max_mem_type1", &self.max_mem_type1)
			// .field("glyph_name_index", &self.glyph_name_index)
			// .field("string_data", &self.string_data)
			// .field("offset", &self.offset)
			.finish()?;
		// for n in self.names() {
		// 	write!(f, "{} ", n)?;
		// }
		Ok(())
	}
}

/// An iterator over glyph names.
///
/// The `post` table doesn't provide the glyph names count,
/// so we have to simply iterate over all of them to find it out.
#[derive(Clone, Copy, Default)]
pub struct Names<'a> {
	data: &'a [u8],
	offset: usize,
}
impl core::fmt::Debug for Names<'_> {
	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
		write!(f, "Names {{ ... }}")
	}
}
impl<'a> Iterator for Names<'a> {
	type Item = &'a str;
	fn next(&mut self) -> Option<Self::Item> {
		// Glyph names are stored as Pascal Strings.
		// Meaning u8 (len) + [u8] (data).

		if self.offset >= self.data.len() {
			return None;
		}

		let len = self.data[self.offset];
		self.offset += 1;

		// An empty name is an error.
		if len == 0 {
			return None;
		}

		let name = self.data.get(self.offset..self.offset + usize::from(len))?;
		self.offset += usize::from(len);
		core::str::from_utf8(name).ok()
	}
}

// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6post.html
/// A list of Macintosh glyph names.
const MACINTOSH_NAMES: &[&str] = &[
	".notdef",
	".null",
	"nonmarkingreturn",
	"space",
	"exclam",
	"quotedbl",
	"numbersign",
	"dollar",
	"percent",
	"ampersand",
	"quotesingle",
	"parenleft",
	"parenright",
	"asterisk",
	"plus",
	"comma",
	"hyphen",
	"period",
	"slash",
	"zero",
	"one",
	"two",
	"three",
	"four",
	"five",
	"six",
	"seven",
	"eight",
	"nine",
	"colon",
	"semicolon",
	"less",
	"equal",
	"greater",
	"question",
	"at",
	"A",
	"B",
	"C",
	"D",
	"E",
	"F",
	"G",
	"H",
	"I",
	"J",
	"K",
	"L",
	"M",
	"N",
	"O",
	"P",
	"Q",
	"R",
	"S",
	"T",
	"U",
	"V",
	"W",
	"X",
	"Y",
	"Z",
	"bracketleft",
	"backslash",
	"bracketright",
	"asciicircum",
	"underscore",
	"grave",
	"a",
	"b",
	"c",
	"d",
	"e",
	"f",
	"g",
	"h",
	"i",
	"j",
	"k",
	"l",
	"m",
	"n",
	"o",
	"p",
	"q",
	"r",
	"s",
	"t",
	"u",
	"v",
	"w",
	"x",
	"y",
	"z",
	"braceleft",
	"bar",
	"braceright",
	"asciitilde",
	"Adieresis",
	"Aring",
	"Ccedilla",
	"Eacute",
	"Ntilde",
	"Odieresis",
	"Udieresis",
	"aacute",
	"agrave",
	"acircumflex",
	"adieresis",
	"atilde",
	"aring",
	"ccedilla",
	"eacute",
	"egrave",
	"ecircumflex",
	"edieresis",
	"iacute",
	"igrave",
	"icircumflex",
	"idieresis",
	"ntilde",
	"oacute",
	"ograve",
	"ocircumflex",
	"odieresis",
	"otilde",
	"uacute",
	"ugrave",
	"ucircumflex",
	"udieresis",
	"dagger",
	"degree",
	"cent",
	"sterling",
	"section",
	"bullet",
	"paragraph",
	"germandbls",
	"registered",
	"copyright",
	"trademark",
	"acute",
	"dieresis",
	"notequal",
	"AE",
	"Oslash",
	"infinity",
	"plusminus",
	"lessequal",
	"greaterequal",
	"yen",
	"mu",
	"partialdiff",
	"summation",
	"product",
	"pi",
	"integral",
	"ordfeminine",
	"ordmasculine",
	"Omega",
	"ae",
	"oslash",
	"questiondown",
	"exclamdown",
	"logicalnot",
	"radical",
	"florin",
	"approxequal",
	"Delta",
	"guillemotleft",
	"guillemotright",
	"ellipsis",
	"nonbreakingspace",
	"Agrave",
	"Atilde",
	"Otilde",
	"OE",
	"oe",
	"endash",
	"emdash",
	"quotedblleft",
	"quotedblright",
	"quoteleft",
	"quoteright",
	"divide",
	"lozenge",
	"ydieresis",
	"Ydieresis",
	"fraction",
	"currency",
	"guilsinglleft",
	"guilsinglright",
	"fi",
	"fl",
	"daggerdbl",
	"periodcentered",
	"quotesinglbase",
	"quotedblbase",
	"perthousand",
	"Acircumflex",
	"Ecircumflex",
	"Aacute",
	"Edieresis",
	"Egrave",
	"Iacute",
	"Icircumflex",
	"Idieresis",
	"Igrave",
	"Oacute",
	"Ocircumflex",
	"apple",
	"Ograve",
	"Uacute",
	"Ucircumflex",
	"Ugrave",
	"dotlessi",
	"circumflex",
	"tilde",
	"macron",
	"breve",
	"dotaccent",
	"ring",
	"cedilla",
	"hungarumlaut",
	"ogonek",
	"caron",
	"Lslash",
	"lslash",
	"Scaron",
	"scaron",
	"Zcaron",
	"zcaron",
	"brokenbar",
	"Eth",
	"eth",
	"Yacute",
	"yacute",
	"Thorn",
	"thorn",
	"minus",
	"multiply",
	"onesuperior",
	"twosuperior",
	"threesuperior",
	"onehalf",
	"onequarter",
	"threequarters",
	"franc",
	"Gbreve",
	"gbreve",
	"Idotaccent",
	"Scedilla",
	"scedilla",
	"Cacute",
	"cacute",
	"Ccaron",
	"ccaron",
	"dcroat",
];
